﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.IO.Ports;
using System.Threading;

namespace Walson.IO
{
    /// <summary>
    /// 串口消息解析器
    /// </summary>
    public partial class SerialPortMessengerPicker : Component
    {
        #region 串口方式
        private bool _PortIsOpen;
        private SerialPort _Port;
        #endregion


        #region 通用数据
        private Queue<DataPacket> _ReceiveBuffer;

        #endregion

        /// <summary>
        /// 接收到的数据
        /// </summary>
        public struct DataPacket
        {
            /// <summary>
            /// 源 IP 地址
            /// </summary>
            public string SourceIp;
            /// <summary>
            /// 源端口号
            /// </summary>
            public int SourcePort;
            /// <summary>
            /// 载荷
            /// </summary>
            public byte[] Payload;
        }




        #region 属性和事件
//         /// <summary>
//         /// 指定接口连接方式
//         /// </summary>
//         private Interface PhysicalInterface
//         {
//             get;
//             private set;
//         }

        /// <summary>
        /// 接口方式
        /// </summary>
        private enum Interface
        {
            Null,
            SerialPort,
            UDP,
            TcpServer,
            TcpClient,
        }

        /// <summary>
        /// 串口的端口名，如 COM1，COM2……
        /// </summary>
        public string SerialPortName
        {
            get
            {
                return _Port.PortName;
            }
            set
            {
                if (_PortIsOpen)
                {
                    throw (new Exception("端口已经打开"));
                }
                _Port.PortName = value;
            }
        }

        /// <summary>
        /// 串口的波特率
        /// </summary>
        public int BaudRate
        {
            get;
            set;
        }

        /// <summary>
        /// 数据的包头，必选项
        /// </summary>
        public byte[] PacketHeader
        {
            get;
            set;
        }

        /// <summary>
        /// 数据包长度是否是可变的，必选项
        /// </summary>
        public bool PacketLengthIsVariable
        {
            get;
            set;
        }

        /// <summary>
        /// 数据包中指示包长度数据所在的位置，从包头第 0 个字节开始计算。若 PacketLengthIsVariable 为真，且 PacketTail 为 null，则该属性为必选项。
        /// 若该属性与 PacketTail 均不为 null，则以 PacketTail 所设置的值作为包结束标志，该属性将被忽略。
        /// 注意：解析出的包长度包含包头
        ///      PacketLengthIndicator 中数据的顺序将影响从包中解析包长度的结果，实际上是字节序的问题。
        /// <example>例如：如果PacketLengthIndicator[] = {1,2,3}; 表示包长度在数据包中用 3 个字节存储，位于从包头开始的第1、2、3个字节。</example>
        /// <example>若收到的数据分别为 0x00,0x00,0x33，那么，计算时包长度为 0x000033 个字节；</example>
        /// <example>如果PacketLengthIndicator[] = {3,2,1};，若收到的数据分别为 0x00,0x00,0x33，那么，计算时包长度为 0x330000 个字节。</example>
        /// </summary>
        public byte[] PacketLengthIndicator
        {
            get;
            set;
        }

        /// <summary>
        /// 不包括包头的数据包长度。若 PacketLengthIsVariable 为假，则该属性为必选项。
        /// </summary>
        public int PacketLength
        {
            get;
            set;
        }

        /// <summary>
        /// 包长度修正值。该值用于修正包长度，以适配那些包长度包含包头长度等情况.
        /// 长度修正将直接从预设的包长度或解析出的包长度中减去。修正后的包长度应当是除去包头、包长度字段
        /// 及上述字段间其它字段长度以后数据包的剩余长度。
        /// </summary>
        public int PacketLengthFix
        {
            get;
            set;
        }

        /// <summary>
        /// 包尾，若 PacketLengthIsVariable 为真，且 PacketLengthIndicator 为 null，则该属性为必选项。
        /// </summary>
        public byte[] PacketTail
        {
            get;
            set;
        }

        /// <summary>
        /// 数据包中是否包含指示包长度的字段
        /// </summary>
        public bool HasPacketLengthIndicator
        {
            get;
            set;
        }

        /// <summary>
        /// 表示将处理 PacketReceived 事件的方法
        /// </summary>
        public delegate void PacketReceivedHandler();
        /// <summary>
        /// 数据包到达事件
        /// </summary>
        public event PacketReceivedHandler PacketReceived;
        /// <summary>
        /// 触发数据到达事件
        /// </summary>
        protected void RaisePacketReceived()
        {
            if (PacketReceived != null)
                PacketReceived();
        }
        #endregion

        /// <summary>
        /// 创建消息泵
        /// </summary>
        public SerialPortMessengerPicker()
        {
            InitializeComponent();
            //PhysicalInterface = Interface.Null;
            _PortIsOpen = false;
            _Port = new SerialPort();

            _ReceiveBuffer = new Queue<DataPacket>();
        }

        /// <summary>
        /// 创建串口方式的消息泵。
        /// </summary>
        /// <param name="portName">端口名</param>
        /// <param name="baudRate">波特率</param>
        /// <param name="openPort">是否打开端口</param>
        public SerialPortMessengerPicker(string portName, int baudRate,bool openPort)
        {
            InitializeComponent();
            //PhysicalInterface = Interface.SerialPort;
            _PortIsOpen = false;
            
            _Port = new SerialPort();
            _Port.PortName = portName;
            _Port.BaudRate = baudRate;

            _ReceiveBuffer = new Queue<DataPacket>();

            if (openPort)
                OpenSerialPort();
        }

        /// <summary>
        /// 打开串口
        /// </summary>
        private void OpenSerialPort()
        {
            if (_Port.PortName != string.Empty && _PortIsOpen == false)
            {
                _Port.BaudRate = BaudRate;
                _Port.Open();
                _PortIsOpen = true;
                //PhysicalInterface = Interface.SerialPort;
            }
            else
            {
                _PortIsOpen = false;
                if (_Port.PortName == string.Empty)
                    throw (new Exception("端口号未指定。"));
                if (_PortIsOpen == true)
                    throw (new Exception("端口已经打开。"));
            }
        }

       

        /// <summary>
        /// 启动消息泵
        /// </summary>
        public void Start()
        {
            // 检查包参数
            if (PacketHeader == null)
            {// 如果没有指定包头
                throw (new Exception("没有设置正确的包头。"));
            }

            if (PacketLengthIsVariable)
            {// 变长包
                if (PacketLengthIndicator == null && PacketTail == null)
                {// 变长包没有指定包长度位置
                    throw (new Exception("已声明变长包，但没有给出包长度的位置，且没有指定包尾。"));
                }
            }
            else
            {
                if (PacketLength == 0)
                {// 定长包没有指定包长度
                    throw (new Exception("已声明定长包，但没有给出包长度。"));
                }
            }

            OpenSerialPort();
            _Port.DiscardInBuffer();
            _Port.DiscardOutBuffer();
            _Port.DataReceived += new SerialDataReceivedEventHandler(_Port_DataReceived);

//             switch (PhysicalInterface)
//             {
//                 case Interface.SerialPort:
//                     {
//                         OpenSerialPort();
//                         _Port.DiscardInBuffer();
//                         _Port.DiscardOutBuffer();
//                         _Port.DataReceived += new SerialDataReceivedEventHandler(_Port_DataReceived);
//                         break;
//                     }
//             }
        }

        private void _Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            int bytesToRead = _Port.BytesToRead;
            while (bytesToRead != 0)
            {
                if (PacketLengthIsVariable)
                {
                    if (PacketTail == null)
                    {
                        #region 有长度指示的变长包

                        // 取出包长度指示中表示长度的最大字节和最小字节的位置序号，程序将会一直等到最大字节位置到来以后才会开始处理后面的数据。
                        int max = -1;
                        int min = Int32.MaxValue;
                        if (PacketLengthIndicator.Length == 1)
                        {
                            min = PacketLengthIndicator[0];
                            max = min;
                        }
                        else
                        {
                            foreach (byte b in PacketLengthIndicator)
                            {
                                if (b > max)
                                    max = b;
                                if (b < min)
                                    min = b;
                            }
                        }

                        if (max == -1)
                        {
                            throw (new Exception("数据包长度指示包含非法值。"));
                        }

                        // 如果串口缓冲区中的数据不足以读出包头和包长度，则什么也不做。
                        if (_Port.BytesToRead < PacketHeader.Length + max)
                        {
                            return;
                        }

                        // 声明一个缓冲包
                        Queue<byte> packet = new Queue<byte>();
                        byte temp;
                        // 检查包头
                        for (int i = 0; i < PacketHeader.Length; i++)
                        {
                            temp = (byte)_Port.ReadByte();
                            if (temp != PacketHeader[i])
                                return;

                            packet.Enqueue(temp);
                        }

                        // 如果包头和包长度之间还有其他内容，则读出来放到缓冲包中去
                        byte[] temp2 = new byte[min - PacketHeader.Length];
                        _Port.Read(temp2, 0, temp2.Length);
                        foreach (byte b in temp2)
                        {
                            packet.Enqueue(b);
                        }


                        // 读取剩余包的长度
                        byte[] len = new byte[PacketLengthIndicator.Length];
                        int packetLength = 0;
                        _Port.Read(len, 0, len.Length);
                        foreach (byte b in len)
                        {
                            packet.Enqueue(b);
                        }
                        // 计算剩余包的长度
                        foreach (byte b in PacketLengthIndicator)
                        {
                            packetLength <<= 8;
                            packetLength += len[b - min];
                        }
                        // 修正包长度
                        packetLength -= PacketLengthFix;
                        if (packetLength < 0)
                            return;

                        //如果串口缓冲区的数据不够，则等一会儿。
                        int timeOutCheck = 0;
                        while (_Port.BytesToRead < packetLength)
                        {
                            Thread.Sleep(5);
                            timeOutCheck++;
                            if (timeOutCheck == 10)
                                return;
                        }

                        // 读出剩余的包
                        temp2 = new byte[packetLength];
                        _Port.Read(temp2, 0, temp2.Length);
                        foreach (byte b in temp2)
                        {
                            packet.Enqueue(b);
                        }
                        DataPacket p = new DataPacket();
                        p.Payload = packet.ToArray();
                        _ReceiveBuffer.Enqueue(p);
                        RaisePacketReceived();
                        #endregion
                    }
                    else
                    {
                        #region 有包尾的变长包

                        Queue<byte> packet = new Queue<byte>();
                        byte temp;
                        // 检查包头
                        for (int i = 0; i < PacketHeader.Length; i++)
                        {
                            temp = (byte)_Port.ReadByte();
                            if (temp != PacketHeader[i])
                                return;

                            packet.Enqueue(temp);
                        }

                        int timeOut = 0;
                        while (_Port.BytesToRead < PacketLength)
                        {
                            Thread.Sleep(5);
                            timeOut++;
                            if (timeOut == 10)
                                return;
                        }

                        byte[] tempTail = new byte[PacketTail.Length];
                        bool tailReceived = false;
                        while (tailReceived == false)
                        {
                            for (int i = 0; i < tempTail.Length - 1; i++)
                            {
                                tempTail[i] = tempTail[i + 1];
                            }
                            tempTail[tempTail.Length - 1] = (byte)_Port.ReadByte();
                            packet.Enqueue(tempTail[tempTail.Length - 1]);

                            for (int i = 0; i < PacketTail.Length;i++ )
                            {
                                if (PacketTail[i] == tempTail[i])
                                    tailReceived = true;
                            }
                        }

                        DataPacket p = new DataPacket();
                        p.Payload = packet.ToArray();
                        _ReceiveBuffer.Enqueue(p);
                        RaisePacketReceived();

                        #endregion
                    }
                }
                else
                {
                    #region 定长包

                    // 声明一个缓冲包
                    Queue<byte> packet = new Queue<byte>();
                    byte temp;
                    // 检查包头
                    for (int i = 0; i < PacketHeader.Length; i++)
                    {
                        temp = (byte)_Port.ReadByte();
                        if (temp != PacketHeader[i])
                            return;

                        packet.Enqueue(temp);
                    }

                    int timeOut = 0;
                    while (_Port.BytesToRead < PacketLength)
                    {
                        Thread.Sleep(5);
                        timeOut++;
                        if (timeOut == 10)
                            return;
                    }

                    // 读出剩余字节，不包含包尾
                    byte[] t = new byte[PacketLength];
                    _Port.Read(t, 0, PacketLength);
                    foreach (byte b in t)
                    {
                        packet.Enqueue(b);
                    }

                    DataPacket p = new DataPacket();
                    p.Payload = packet.ToArray();
                    _ReceiveBuffer.Enqueue(p);
                    RaisePacketReceived();
                    #endregion
                }
            }
        }

        /// <summary>
        /// 读取一个数据包
        /// </summary>
        /// <returns>数据包</returns>
        public DataPacket ReadPacket()
        {
            return _ReceiveBuffer.Dequeue();
        }
    }
}
